package cn.school.api.service.impl;

import cn.school.api.bo.StudentBO;
import cn.school.api.service.ExternalInterfaceService;
import cn.school.attendance.dormitory.dao.AttendanceRulesDao;
import cn.school.attendance.dormitory.dao.DormitoryAttendanceMapper;
import cn.school.attendance.dormitory.entity.DormitoryAttendanceDo;
import cn.school.common.utils.StringUtils;
import cn.school.student.dao.StudentGuardianDao;
import cn.school.student.dao.StudentInfoDao;
import cn.school.student.dao.StudentTrajectoryMapper;
import cn.school.student.entity.StudentGuardian;
import cn.school.student.entity.StudentInfo;
import cn.school.student.entity.StudentTrajectory;;
import cn.school.wechat.utils.WeChatParamUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: lx
 * @date: 2024/1/9 9:00
 * @description:
 */
@Slf4j
@Service
public class ExternalInterfaceServiceImpl implements ExternalInterfaceService {



    @Resource
    private StudentInfoDao studentInfoDao;

    @Autowired
    private StudentGuardianDao studentGuardianDao;

    @Resource
    private StudentTrajectoryMapper studentTrajectoryMapper;

    @Resource
    private DormitoryAttendanceMapper dormitoryAttendanceMapper;

    @Resource
    private AttendanceRulesDao rulesDao;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    public Boolean receiveSchoolGateEntryAndExitInfo(List<Map<String, String>> records) {
        //1.根据userId即学生id进行分组
        Map<String, List<Map<String, String>>> groupByUserId = records.stream()
                .collect(Collectors.groupingBy(item -> item.get("userId")));

        //2.获取分组后的id
        Set<String> keySet = groupByUserId.keySet();
        List<String> stuIdList = new ArrayList<>(keySet);

        //3.通过id去查询出学生姓名，家长的openId集合，租户id，是否发送微信通知，以map的形式暂存，key=学生id，value=于数据中查的数据
        Map<String,StudentBO> db = new HashMap<>();
        for(String studentId : stuIdList){
            StudentBO studentBO = studentInfoDao.getResultRelateSchoolGate(studentId);
            if(studentBO != null){
                db.put(studentId,studentBO);
            }
        }

        List<StudentTrajectory> trajectoryList = new ArrayList<>();

        //4.遍历传入数据集合
        for(Map<String,String> map : records){
            log.info("========>  ReceiveSchoolGateEntryAndExitInfoStart");

            String studentId = map.get("userId");
            String date = map.get("stuTime");
            String deviceName = map.get("deviceName");
            String snapFtpUrl = map.get("snapFtpUrl");
            String direction = map.get("direction");

            StudentBO studentBO = db.get(studentId);
            if(studentBO == null){
                log.info("无可匹配数据，id：{}，进出时间：{}，打卡设备：{}，进出方向：{}",
                        studentId,date,deviceName,direction);
                log.info("========>  ReceiveSchoolGateEntryAndExitInfoStart");
                continue;
            }
            log.info("当前学生id：{}，学生姓名：{}，进出时间：{}，打卡设备：{}，进出方向：{}",
                    studentId,studentBO.getName(),date,deviceName,direction);

            //5.判断是否需要发送微信通知 0-关闭通知 1-开启通知
            if("1".equals(studentBO.getIsEnableNotice())) {
                String studentName = studentBO.getName();

                List<String> openIdList = null;
                if(StringUtils.isNotEmpty(studentBO.getOpenIdList())) {
                    openIdList = Arrays.asList(studentBO.getOpenIdList().split(","));
                }

                if (openIdList == null || openIdList.size() <= 0) {
                    log.info("当前学生监护人openid为空，无法送微信通知");
                }else {
                    //6.根据进出方向获取模板id
                    String templateId = null;
                    if("1".equals(direction)){
                        templateId = "icYCvx7OjZsJdlXaFZYpCFVuUfxRuJZNoCQ4LJg0jMc";
                    }else if("2".equals(direction)){
                        templateId = "I6lFcc5tvJkHISicynu3xUO0Cehow-XWOSlpcrIbNOg";
                    }
                    //7.存在有模板id时候，则发送微信通知
                    if(StringUtils.isNotEmpty(templateId)){
                        Map<String, Object> msg = WeChatParamUtil.getArriveOrLeaveSchoolParam(studentName,
                                deviceName, date);
                        for (String openId : openIdList) {
                            try {
                                //8.先从缓存查看当前openId是否一分钟内发送过微信通知
                                String isSend = redisTemplate.opsForValue().get("school-gate-msg-send-" + studentId + openId);
                                if(StringUtils.isEmpty(isSend)){


                                    redisTemplate.opsForValue().set("school-gate-msg-send-" + studentId + openId, "true");
                                    redisTemplate.expire("school-gate-msg-send-" + studentId + openId, 1, TimeUnit.MINUTES);
                                }else {
                                    log.info("当前学生同分钟内已发送微信通知,{},{}",studentId,openId);
                                }
                            } catch (Exception e) {
                                log.info("微信公众号消息推送失败,{}",e.getMessage());
                            }
                        }
                    }else {
                        log.info("未找到通知模板id");
                    }
                }
            }else {
                log.info("未配置公众号通知参数(weChat.notice.enable)或者未开启公众号通知");
            }

            StudentTrajectory studentTrajectory = new StudentTrajectory();
            studentTrajectory.setStudentId(Long.parseLong(studentId));
            studentTrajectory.setDeviceAddr(deviceName);
            studentTrajectory.setFaceTime(date);
            studentTrajectory.setSnapFtpUrl(snapFtpUrl);
            studentTrajectory.setType(direction);
            studentTrajectory.setCreateTime(new Date());
            studentTrajectory.setTenantId(studentBO.getTenantId());
            trajectoryList.add(studentTrajectory);
            log.info("========>  ReceiveSchoolGateEntryAndExitInfoEnd");
        }
//        List<StudentTrajectory> dpList = deduplication2(trajectoryList);
//        log.info("TrajectoryList去重【前】数据量:{},去重【后】数据:{}" ,trajectoryList.size(),dpList.size());
        List<List<StudentTrajectory>> partition = ListUtils.partition(trajectoryList, 100);
        for (List<StudentTrajectory> item : partition) {
            studentTrajectoryMapper.insertBatch(item);
        }
        return true;
    }

    @Override
    public Boolean receiveDormitoryCheckInfo(List<Map<String, String>> records) {
        //1.根据根据userId分组
        Map<String, List<Map<String, String>>> groupByUserId = records.stream()
                .collect(Collectors.groupingBy(item -> item.get("userId")));

        //2.获取分组后的id
        Set<String> keySet = groupByUserId.keySet();
        List<String> stuIdList = new ArrayList<>(keySet);

        //3.通过id去查询出学生姓名，家长的openId集合，租户id，是否发送微信通知，考勤规则，以map的形式暂存，key=学生id，value=于数据中查的数据
        Map<String,StudentBO> db = new HashMap<>();
        for(String studentId : stuIdList){
            StudentBO studentBO = studentInfoDao.getResultRelateDormitoryCheck(studentId);
            if(studentBO != null){
                db.put(studentId,studentBO);
            }
        }

        List<StudentTrajectory> trajectoryList = new ArrayList<>();
        List<DormitoryAttendanceDo> attendanceList = new ArrayList<>();

        //4.遍历传入数据集合
        for(Map<String,String> map : records){
            log.info("========>  ReceiveDormitoryCheckInfoStart");

            String stuId = map.get("userId");
            String date = map.get("stuTime");
            String snapFtpUrl = map.get("snapFtpUrl");
            String deviceName = map.get("deviceName");
            String serialNo = map.get("deviceCode");

            StudentBO studentBO = db.get(stuId);
            if(studentBO == null){
                log.info("无可匹配数据，id：{}，考勤时间：{}，考勤设备：{}，设备序列号：{}",
                        stuId,date,deviceName,serialNo);
                log.info("========>  ReceiveDormitoryCheckInfoEnd");
                continue;
            }
            log.info("当前学生id：{}，学生姓名：{}，考勤时间：{}，考勤设备：{}，设备序列号：{}",
                    stuId,studentBO.getName(),date,deviceName,serialNo);

            if(StringUtils.isEmpty(studentBO.getAttendanceStartTime())
                    && StringUtils.isEmpty(studentBO.getAttendanceEndTime())){
                log.info("未配置宿舍考勤规则");
                log.info("========>  ReceiveDormitoryCheckInfoEnd");
                continue;
            }

            String status = judgeAttendanceStatus(date,studentBO.getAttendanceStartTime(),studentBO.getAttendanceEndTime());
            if("-1".equals(status)){
                log.info("该学生未在指定时间打卡");
                log.info("========>  ReceiveDormitoryCheckInfoEnd");
                continue;
            }
            if("1".equals(studentBO.getIsEnableNotice())){
                String studentName = studentBO.getName();

                List<String> openIdList = null;
                if(StringUtils.isNotEmpty(studentBO.getOpenIdList())) {
                    openIdList = Arrays.asList(studentBO.getOpenIdList().split(","));
                }

                if (openIdList == null || openIdList.size() <= 0) {
                    log.info("当前学生监护人openid为空，无法送微信通知");
                }else {
                    Map<String, Object> msg = WeChatParamUtil.getAttendanceResultParam(studentName, "宿舍归寝",
                            deviceName.split("-")[0], "正常打卡", date);
                    for (String openId : openIdList) {
                        try {
                            //先从缓存查看当前openId是否一分钟内发送过微信通知
                            String isSend = redisTemplate.opsForValue().get("dormitory-attendance-msg-send-" + stuId + openId);
                            if(StringUtils.isEmpty(isSend)){


                                redisTemplate.opsForValue().set("dormitory-attendance-msg-send-" + stuId + openId, "true");
                                redisTemplate.expire("dormitory-attendance-msg-send-" + stuId + openId, 1, TimeUnit.MINUTES);
                            }else {
                                log.info("当前学生同分钟内已发送微信通知,{},{}",stuId,openId);
                            }
                        } catch (Exception e) {
                            log.info("微信公众号消息推送失败,{}",e.getMessage());
                        }
                    }
                }
            }

            //学生行动轨迹数据
            StudentTrajectory studentTrajectory = new StudentTrajectory();
            studentTrajectory.setStudentId(Long.parseLong(stuId));
            studentTrajectory.setDeviceAddr(deviceName);
            studentTrajectory.setFaceTime(date);
            studentTrajectory.setSnapFtpUrl(snapFtpUrl);
            studentTrajectory.setType("3");
            studentTrajectory.setCreateTime(new Date());
            studentTrajectory.setTenantId(studentBO.getTenantId());
            trajectoryList.add(studentTrajectory);

            //更新考勤数据
            DormitoryAttendanceDo attendanceDo = new DormitoryAttendanceDo();
            attendanceDo.setStudentId(Long.parseLong(stuId));
            attendanceDo.setStatus("1");
            attendanceDo.setAttendanceDate(date);
            attendanceList.add(attendanceDo);
            log.info("========>  ReceiveDormitoryCheckInfoEnd");
        }

        log.info("数据总量：" + records.size());
        log.info("成功数据：" + attendanceList.size());
        //批量插入数据至学生轨迹表中
//        List<StudentTrajectory> dpList = deduplication2(trajectoryList);
//        log.info("TrajectoryList去重【前】数据量:{},去重【后】数据:{}" ,trajectoryList.size(),dpList.size());
        List<List<StudentTrajectory>> partition1 = ListUtils.partition(trajectoryList, 100);
        for (List<StudentTrajectory> item : partition1) {
            studentTrajectoryMapper.insertBatch(item);
        }
        //批量更新学生考勤信息
        List<List<DormitoryAttendanceDo>> partition2 = ListUtils.partition(attendanceList, 100);
        for (List<DormitoryAttendanceDo> item : partition2) {
            dormitoryAttendanceMapper.updateBatch(item);
        }
        return true;
    }

    @Override
    public Boolean receiveImgSecondPush(List<Map<String, String>> records) {
        int success = 0;
        int fail = 0;
        for(Map<String,String> map : records){
            String studentId = map.get("cardNo");
            String faceTime = map.get("faceTime");
            String snapFtpUrl = map.get("imgOssUrl");

            int result = studentTrajectoryMapper.updateSnapByStuIdAndFaceTime(studentId, faceTime, snapFtpUrl);
            if(result > 0){
                log.info("【{}】学生照片更新失败",studentId);
                success++;
            }else {
                fail ++;
            }
        }
        log.info("照片更新成功数量，{}",success);
        log.info("照片更新失败数量，{}",fail);
        return true;
    }

    @Override
    public Map<String, Object> getSomeInfoForOutByOpenId(Map<String,String> param) {
        Map<String, Object> resultMap = new HashMap<>();
        String accessKey = param.get("accessKey");
        String accessSecret = param.get("accessSecret");
        if(!("public-phone".equals(accessKey) && "f1c9020a1c15c495".equals(accessSecret))){
            resultMap.put("code", 403);
            resultMap.put("msg", "没有权限");
            return resultMap;
        }
        String outOpenId = param.get("outOpenId");
        if(StringUtils.isEmpty(outOpenId)){
            resultMap.put("code", 500);
            resultMap.put("msg", "缺少参数");
            return resultMap;
        }
        // 根据家长的openId查询出学生信息
        LambdaQueryWrapper<StudentInfo> lqw = new LambdaQueryWrapper<>();
        lqw.eq(StudentInfo::getGuardianOfficialOpenId, outOpenId);
        List<StudentInfo> studentInfoList = studentInfoDao.selectList(lqw);
        resultMap.put("code", 0);
        resultMap.put("msg", "success");
        List<Map<String, Object>> userList = new ArrayList<>();

        try {
            for (StudentInfo studentInfo : studentInfoList) {
                LambdaQueryWrapper<StudentGuardian> lqwGuardian = new LambdaQueryWrapper<>();
                lqwGuardian.eq(StudentGuardian::getStudentId, studentInfo.getId());
                List<StudentGuardian> studentGuardians = studentGuardianDao.selectList(lqwGuardian);
                Map<String, Object> userMap = new HashMap<>();
                userMap.put("updateKey", studentInfo.getId());
                userMap.put("userName", studentInfo.getName());
                userMap.put("relation", studentInfo.getRelationOfGuardian());
                userMap.put("outOrgId", studentInfo.getTenantId());
                userMap.put("cardNumber", studentInfo.getId());
                userList.add(userMap);

                for (StudentGuardian studentGuardian : studentGuardians) {
                    userMap = new HashMap<>();
                    userMap.put("updateKey", studentInfo.getId());
                    userMap.put("userName", studentInfo.getName());
                    userMap.put("relation", studentGuardian.getStudentRelation());
                    userMap.put("outOrgId", studentInfo.getTenantId());
                    userMap.put("cardNumber", studentInfo.getId());
                    userList.add(userMap);
                }
            }
            resultMap.put("userList", userList);
        } catch (Exception e) {
            resultMap.put("code", 500);
            resultMap.put("msg", "failed");
        }
        return resultMap;
    }

    /**
     *
     * @param targetTime 目标时间(yyyy-MM-dd HH:mm:ss)
     * @param start 起始时间(HH:mm)
     * @param end 结束时间(HH:mm)
     * @return
     * @throws Exception
     */
    private String judgeAttendanceStatus(String targetTime,String start,String end){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        StringBuilder startTime = new StringBuilder(targetTime.split(" ")[0]);
        startTime.append(" ").append(start).append(":00");
        StringBuilder endTime = new StringBuilder(targetTime.split(" ")[0]);
        endTime.append(" ").append(end).append(":00");
        long targetMilliseconds = 0;
        long startMilliseconds = 0;
        long endMilliseconds = 0;
        try {
            targetMilliseconds = sdf.parse(targetTime).getTime();
            startMilliseconds = sdf.parse(startTime.toString()).getTime();
            endMilliseconds = sdf.parse(endTime.toString()).getTime();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if(targetMilliseconds >= startMilliseconds && targetMilliseconds <= endMilliseconds){
            return "1";
        }else {
            return "-1";
        }
    }

    /**
     * 去掉重复数据
     * @param list 目标list
     * @return
     */
//    private List<StudentTrajectory> deduplication(List<StudentTrajectory> list){
//        Set<StudentTrajectory> setData = new HashSet<>();
//        setData.addAll(list);
//        return setData.stream().collect(Collectors.toList());
//    }

    private List<StudentTrajectory> deduplication2(List<StudentTrajectory> list){
        Map<String, StudentTrajectory> map = new HashMap<>();
        for(StudentTrajectory item : list){
            map.put(item.getStudentId() + item.getDeviceAddr()
                    + item.getFaceTime().substring(0,item.getFaceTime().length()-3)
                    + item.getType() + item.getTenantId(),item);
        }
        List<StudentTrajectory> list2 = new ArrayList<>();
        Set<Map.Entry<String, StudentTrajectory>> entries = map.entrySet();
        Iterator<Map.Entry<String, StudentTrajectory>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<String, StudentTrajectory> next = iterator.next();
            list2.add(next.getValue());
        }
        return list2;
    }
}
